﻿using System.Collections.Concurrent;
using System.Diagnostics;
using System.Net.Http.Headers;
using System.Net.Http.Json;

namespace xrwang.DDNS.DNSExit
{
    /// <summary>
    /// DNSExit域名记录操作
    /// </summary>
    public class DomainRecordOperator : IDomainRecordOperator
    {
        #region Const
        /// <summary>
        /// 服务器地址
        /// </summary>
        private const string URL = "https://api.dnsexit.com/dns/ud/?apikey={0}";
        /// <summary>
        /// 云服务提供商
        /// </summary>
        private const CSPEnum CSP = CSPEnum.DNSExit;
        /// <summary>
        /// 默认的解析生效时间（4分钟）
        /// </summary>
        private const int DEFAULT_TTL = 4;
        #endregion Const

        #region Static
        /// <summary>
        /// HttpClient对象
        /// </summary>
        private static HttpClient httpClient = new HttpClient(new SocketsHttpHandler()
        {
            PooledConnectionLifetime = TimeSpan.FromMinutes(2)
        });
        /// <summary>
        /// 最近调用时间字典，键为apikey，值为该apikey对应的最近调用时间
        /// </summary>
        private static ConcurrentDictionary<string, DateTime> dictLastCallTime = new ConcurrentDictionary<string, DateTime>();
        /// <summary>
        /// API调用间隔（2分钟）
        /// </summary>
        private static readonly TimeSpan interval = new TimeSpan(0, 2, 0);
        #endregion Static

        /// <summary>
        /// 密匙id
        /// </summary>
        private string apiKey;
        /// <summary>
        /// 密匙id
        /// </summary>
        public string ApiKey
        {
            get
            {
                return apiKey;
            }
            set
            {
                if (string.IsNullOrWhiteSpace(value))
                    throw new ArgumentNullException(nameof(ApiKey), string.Format("{0}不能为空。", nameof(ApiKey)));
                apiKey = value;
            }
        }
        /// <summary>
        /// 是否等待API调用间隔
        /// </summary>
        public bool WaitForAPICallingInterval { get; set; }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="apiKey">密匙id</param>
        /// <param name="waitForAPICallingInterval">是否等待API调用间隔</param>
        public DomainRecordOperator(string apiKey, bool waitForAPICallingInterval = true)
        {
            ApiKey = apiKey;
            WaitForAPICallingInterval = waitForAPICallingInterval;
        }

        /// <summary>
        /// 发送请求
        /// </summary>
        /// <param name="rr">主机记录</param>
        /// <param name="domainName">域名</param>
        /// <param name="value">值</param>
        /// <returns>返回结果</returns>
        private async Task<ActionResult> Request(string rr, string domainName, string value)
        {
            DateTime lastCallTime;
            if (dictLastCallTime.TryGetValue(apiKey, out lastCallTime))
            {
                TimeSpan timeSpan = DateTime.Now - lastCallTime;
                if (timeSpan < interval)
                {
                    if (!WaitForAPICallingInterval)
                        return new ActionResult(false, string.Format("达到API调用间隔限制，上次调用时间：{0}", lastCallTime));
                    await Task.Delay(interval - timeSpan);
                }
            }
            dictLastCallTime[apiKey] = DateTime.Now;
            string url = string.Format(URL, apiKey);
            string host;
            if (string.IsNullOrEmpty(rr))
                host = domainName;
            else if (rr.EndsWith(domainName))
                host = rr;
            else
                host = string.Format("{0}.{1}", rr, domainName);
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
            request.Content = new StringContent(string.Format("host={0}&ip={1}", host, value));
            request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
            try
            {
                HttpResponseMessage response = await httpClient.SendAsync(request);
                if (!response.IsSuccessStatusCode)
                    return new ActionResult(false, string.Format("服务器返回响应代码：{0}", response.StatusCode));
                ServerReply reply = await response.Content.ReadFromJsonAsync<ServerReply>();
                return new ActionResult(reply.Success, reply.Success ? reply.message : string.Format("{0}-{1}", reply.code, reply.message));
            }
            catch (Exception ex)
            {
                string message = string.Format("在发送请求时发生异常。{0}服务器地址：{1}{0}异常：{2}",
                    Environment.NewLine, URL, ex);
                Debug.WriteLine(message);
                return new ActionResult(false, message);
            }
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="recordId">记录id（忽略该参数）</param>
        /// <param name="domainName">域名（子域名）</param>
        /// <returns>返回结果。如果成功，Data为请求id。</returns>
        /// <remarks>Data1不会返回值。</remarks>
        public async Task<ActionResult<string>> DeleteAsync(string recordId, string domainName)
        {
            return new ActionResult<string>(false, string.Format("{0:g}未提供删除这种域名记录的方法。", CSP));
        }

        /// <summary>
        /// 获取域名记录列表
        /// </summary>
        /// <param name="domainName">域名名称</param>
        /// <param name="rr">主机记录</param>
        /// <param name="type">记录类型</param>
        /// <param name="value">记录值</param>
        /// <param name="line">解析线路</param>
        /// <param name="page">页码</param>
        /// <param name="pageSize">每页记录数</param>
        /// <returns>返回结果。如果成功，Data1为域名记录信息列表，Data2为记录总数。</returns>
        /// <remarks>Data1、Data2不会返回值。</remarks>
        public async Task<ActionResult<List<DomainRecordInfo>, int>> GetListAsync(string domainName, string rr = "", DomainRecordTypeEnum? type = null, string value = "", string line = "", int page = 1, int pageSize = 100)
        {
            return new ActionResult<List<DomainRecordInfo>, int>(false, string.Format("{0:g}未提供获取域名记录的方法。", CSP));
        }

        /// <summary>
        /// 设置状态
        /// </summary>
        /// <param name="recordId">记录id</param>
        /// <param name="domainName">域名名称（阿里云域名记录删除接口不需要该参数）</param>
        /// <returns>返回结果。如果成功，Data为请求id。</returns>
        public async Task<ActionResult<string>> SetStatusAsync(string recordId, string domainName = "", bool enabled = true)
        {
            return new ActionResult<string>(false, string.Format("{0:g}未提供设置状态的方法。", CSP));
        }

        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="record">域名记录</param>
        /// <returns>返回结果。如果成功，Data1为请求id，Data2为记录id。</returns>
        /// <remarks>Data1、Data2不会返回值。</remarks>
        public async Task<ActionResult<string, string>> AddAsync(DomainRecordInfo record)
        {
            var obj = new
            {
                apikey = ApiKey,
                domain = record.DomainName,
                add = new
                {
                    type = record.Type,
                    name = record.RR,
                    content = record.Value,
                    priority = record.Priority,
                    ttl = record.TTL.HasValue ? record.TTL.Value / 60 : DEFAULT_TTL,
                    overwrite = true
                }
            };
            ActionResult result = await Request(record.RR, record.DomainName, record.Value); //await Request(obj);
            return new ActionResult<string, string>(result.Success, result.Message);
        }

        /// <summary>
        /// 修改
        /// </summary>
        /// <param name="record">域名记录</param>
        /// <returns>返回结果。如果成功，Data1为请求id，Data2为记录id。</returns>
        public async Task<ActionResult<string, string>> UpdateAsync(DomainRecordInfo record)
        {
            var obj = new
            {
                apikey = ApiKey,
                domain = record.DomainName,
                update = new
                {
                    type = record.Type,
                    name = record.RR,
                    content = record.Value,
                    priority = record.Priority,
                    ttl = record.TTL.HasValue ? record.TTL.Value / 60 : DEFAULT_TTL
                }
            };
            ActionResult result = await Request(record.RR, record.DomainName, record.Value); //await Request(obj);
            return new ActionResult<string, string>(result.Success, result.Message);
        }

        /// <summary>
        /// 保存
        /// </summary>
        /// <param name="record">域名记录</param>
        /// <returns>返回结果。如果成功，Data1为请求id，Data2为记录id。</returns>
        public async Task<ActionResult<string, string>> SaveAsync(DomainRecordInfo record)
        {
            return await UpdateAsync(record);
        }
    }
}